import re

"""Valmogrify performs macro substitution on the input string. The syntax and some of this text is stolen from the 
    Tilton Macro processor http://www.crockford.com/tilton/tilton.html. It is quite astonishingly ugly, and the 
    author gleefully notes: "It is an even uglier language than Lisp because it replaces the parens with angle brackets
    and tildes while lacking any useful data structures."

    Macro expressions and arguments are completely contained within a set of macro brackets: <~ ~> 
    Macro arguments are separated by tildes. 
    Tilton only uses the three characters '<~>'. All other characters are treated literally, even whitespace. So your
    macros can have names consisting entirely of whitespace. Not even Forth allows you to this! This makes Tilton 
    uncommonly good at dealing with textual content.    
    Macros are called with the syntax <~MACRO~ARG1~ARG2 ... ~>. The 
    substitutions are defined in a dictionary. E.g. 
      valmogrify('foo<~bar~>baz', {'bar': 'GRUNTIES'}) -> 'fooGRUNTIESbaz'.
"""
RE_MACRO = re.compile(r'<~(.*?)~>', re.S) 
RE_MACRO_ARG = re.compile(r'<~(\d+)~>')
def valmogrify(s, symbols):
    def subber(m):
        sol = s.rfind('\n', 0, m.start())
        indent = s[sol+1:m.start()]
        if indent and not indent.isspace():
            indent = ''
        #print repr(indent)
        bits = m.group(1).split('~')
        macro_name = bits[0]
        macro_text = symbols[macro_name]
        def sub_args(m):
            argno = int(m.group(1))
            try:
                repl = bits[argno].splitlines()
                indented_repl = '\n'.join([repl[0]] + ['%s%s' % (indent, x) for x in repl[1:]])
                #print repr(indented_repl)
                return indented_repl
            except IndexError:
                return '<none>'
        while 1:
            macro_text, n = RE_MACRO_ARG.subn(sub_args, macro_text)
            if not n:
                return macro_text
    while 1:
        s, n = RE_MACRO.subn(subber, s)
        if not n:
            return s
            
print(repr(valmogrify('asdasd<~foo~> \n*<~foo~baz~>\n  <~foo~asdasd\nfdf~>sd', { 'a': 'zzz', 'foo': 'bar<~a~>' })))            
print(repr(valmogrify('x<~foo<~a~>~>x', { 'a': 'zzz', 'foo': 'bar<~a~>' })))            

SOURCE_TEMPLATE_START = """\
/* This file is auto-generated. Do not edit. */

<~VERBATIM_INCLUDE~>

/* Context type declaration */
typedef struct {
<~CONTEXT_DECL~>
} smk_context_<~MACHINE_NAME~>_t;

/* State ID declaration. */
enum {
    <~STATE_DECL~>
};

static smk_context_<~MACHINE_NAME~>_t context;

<~VERBATIM_CODE~>

/* Some handy macros. */
#define PROP(member_) (context.member_)
#define change_state_hook(st_) <~CHANGE_STATE_HOOK)
#define SMK_CHANGE_STATE(st_) do { change_state_hook(st_); PROP(state_) = (st_); } while(0)

void smk_process_<~MACHINE_NAME)(<~EVENT_REFERENCE_TYPE) ev) {
    if(<~EVENT_ACCESSOR) == <~RESET_EVENT_NAME)) {
        SMK_CHANGE_STATE(<~STATE_NAME_PREFIX)<~INITIAL_STATE));
<~RESET_FUNCTION_BODY)
        return;
    }
    
    switch(context.state_) {
"""
SOURCE_TEMPLATE_END = """\
    }
}

/* EOF */
"""